#pragma once
#include <SDL3/SDL.h>
#include <SDL3/SDL_vulkan.h>
#include <vulkan/vulkan.hpp>

#include <iostream>
#include <memory>
#include <vector>
#include <optional>

namespace core
{
    class Context final
    {
    private:
        static std::unique_ptr<Context> instance_;
        vk::Instance vlkInstance;
        VkSurfaceKHR surface;
        vk::PhysicalDevice phyDevice;
        vk::Device device;
        vk::Queue graphicQueue;
        vk::Queue presentQueue;
        vk::Extent2D swapChainExtent;
        vk::SwapchainKHR swapchain;
        // vk::Image *swapchainImage;
        uint32_t swapchainImagesCount;
        vk::Format format;
        std::vector<vk::Image> swapchainImages;
        std::vector<vk::ImageView> imageViews;
        vk::RenderPass renderPass;
        vk::DescriptorSetLayout descriptorSetLayout;
        vk::Pipeline pipeline;

        struct QueueFamilyIndicates final
        {
            std::optional<uint32_t> graphicFamily;
            std::optional<uint32_t> presentFamily;;
            std::optional<bool> hasGraphicFamily;;
            std::optional<bool> hasPresentFamily;;
        };
        QueueFamilyIndicates queueFamilyIndicates;
        struct SwapChainSupportDetails final
        {
            vk::SurfaceCapabilitiesKHR capabilities;
            std::vector<vk::SurfaceFormatKHR> formats;
            std::vector<vk::PresentModeKHR> presentModes;
        };
        SwapChainSupportDetails swapChainSupportDetails;
        

        Context(SDL_Window *window);
        char const *const *getRequiredExtensions(uint32_t *count);
        void createVulkanInstance();
        vk::Instance &GetVkInstance();
        VkSurfaceKHR &GetSurface();
        void createSurface(SDL_Window *window);
        void pickPhycicalDevice();
        void findQueueFamilyIndicates(vk::PhysicalDevice device);
        void findSwapChainSupported(vk::PhysicalDevice phyDevice, VkSurfaceKHR surface);
        void createDevice();
        void getDeviceQueue();
        void createSwapchain(SDL_Window *window);
        vk::Extent2D chooseSwapExtent(SDL_Window *window, vk::SurfaceCapabilitiesKHR *capabilities);
        void createImageViews();
        void createRenderPass();
        void setUpUniformBufferLayout();
        void createGraphicsPipeline();
    public:
        ~Context();
        static void Init(SDL_Window *window);
        static void Quit();
    };
}